//
//  NetworkManager.swift
//  ShortVideo
//
//  Created by SYQM on 2022/8/3.
//

// https://www.jianshu.com/p/1e4ed5496950

import Foundation
import Moya
import Alamofire
import SwiftyJSON

///超时时长
private var requestTimeOut:Double = 30

// 回调 包括：网络请求的模型(code,message,data等，具体根据业务来定)
typealias RequestResultClosure = ((ResponseModel) -> Void)

///先添加一个闭包用于成功时后台返回数据的回调
//typealias successCallback = ((String) -> (Void))
//typealias failureCallback = ((String) -> (Void))

/// dataKey一般是 "data"
let dataKey = "data"
let messageKey = "msg"
let codeKey = "code"
let successCode: Int = 200




class ResponseModel {
    var isErrNetwork : Bool = false
    var code: Int = -999
    var message: String = ""
    // 这里的data用String类型 保存response.data
    var data: String = ""
}

/// endpointClosure
private let myEndpointClosure = { (target : TargetType) -> Endpoint in
    ///这里的endpointClosure和网上其他实现有些不太一样。
    ///主要是为了解决URL带有？无法请求正确的链接地址的bug
    let url = target.baseURL.absoluteString + target.path
    var endpoint = Endpoint(
        url: url,
        sampleResponseClosure: {
            .networkResponse(200, target.sampleData)
        },
        method: target.method,
        task: target.task,
        httpHeaderFields: target.headers)
    
    requestTimeOut = 30 // 每次请求都会调用endpointClosure 到这里设置超时时长 也可单独每个接口设置
    
    // 针对于某个具体的业务模块来做接口配置
    if let apiTarget = target as? API {
        switch apiTarget {
        case .register:
            requestTimeOut = 5
            return endpoint
            
        default:
            return endpoint
        }
    }
    
    return endpoint.adding(newHTTPHeaderFields: ["Accept-Language":"zh-Hans-CN",
//                                                 "token" : LoginModelTools.shareInstance.model.token ,
//                                                 "deviceId" : "9E726A1256C2F178FE72",
//                                                 "loginType" : "1",
//                                                 "mobileModel" : "iPhone 7",
//                                                 "os" : "14.2",
//                                                 "platform" : "IOS",
//                                                 "platformCode" : "xuanhe",
//                                                 "timesRequest" : "1131214.393186",
//                                                 "version" : "1.1.0",
//                                                 "versionCode" : "10",
                                         ])
}


private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
    do {
        var request = try endpoint.urlRequest()
        request.timeoutInterval = requestTimeOut
        //打印请求参数
        if let requestData = request.httpBody {
            print("\(request.url!)"+"\n"+"\(request.httpMethod ?? "")"+"发送参数"+"\n"+"\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
        }else{
            print("\(request.url!)"+"\(String(describing: request.httpMethod))")
        }
        
        if let header = request.allHTTPHeaderFields {
            print("请求头内容\(header)")
        }
        done(.success(request))
        
    } catch  {
        done(.failure(MoyaError.underlying(error, nil)))
    }
}


/// NetworkActivityPlugin插件用来监听网络请求
private let networkPlugin = NetworkActivityPlugin.init { changeType, TargetType in
    print("networkPlugin \(changeType)")
    
    //TargetType 是当前请求的基本信息
    switch (changeType){
    case .began :
        print("\n")
        print(TargetType)
        print("\n")
        print("开始请求网络")
    case .ended :
        print("网络请求结束")
    }
}

/// 网络请求发送的核心初始化方法，创建网络请求对象
fileprivate let Provider = MoyaProvider<MultiTarget>(endpointClosure: myEndpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)

/// 请求方法
/// - Parameters:
///   - target: TargetType
///   - successCallback: 成功回调
///   - failureCallback: 失败回调
/// - Returns: 请求操作
@discardableResult
func NetWorkRequest(_ target: TargetType, successCallback:@escaping RequestResultClosure, failureCallback: RequestResultClosure? = nil) -> Cancellable? {
    // 先判断网络是否有链接 没有的话直接返回--代码略
    if !UIDevice.isNetworkConnect {
        // code = 9999 代表无网络  这里根据具体业务来自定义
        errorHandler(code: 9999,isErrNetwork: true, message: "网络似乎出现了问题", failure: failureCallback)
        return nil
    }
    return Provider.request(MultiTarget(target)) { result in
        switch result {
        case let .success(response):
            do {
                let jsonData = try JSON(data: response.data)
                print("返回结果是：\(jsonData)")
                //改行代码为项目返回结果自测,可根据情况处理
                if !validateRepsonse(response: jsonData.dictionary, failure: failureCallback) { return }
                let respModel = ResponseModel()
                /// 这里的 -999的code码 需要根据具体业务来设置
                respModel.code = jsonData[codeKey].int ?? -999
                respModel.message = jsonData[messageKey].stringValue
//                respModel.isSuccess = true
                if respModel.code == successCode {
                    respModel.data = jsonData[dataKey].rawString() ?? ""
                    successCallback(respModel)
                } else {
                    errorHandler(code: respModel.code,isErrNetwork:false , message: respModel.message , failure: failureCallback)
                    return
                }
            } catch {
                // code = 1000000 代表JSON解析失败  这里根据具体业务来自定义
                errorHandler(code: 1000000,isErrNetwork: false, message: String(data: response.data, encoding: String.Encoding.utf8)!, failure: failureCallback)
            }
        case let .failure(error as NSError):
            errorHandler(code: error.code,isErrNetwork: true, message: "网络连接失败", failure: failureCallback)
        }
    }
}


/// 基于Alamofire，网络是否连接，这个方法不建议放到这个类中,可以放在全局的工具类中判断网络链接情况
/// 用计算型属性是因为这样才会在获取isNetworkConnect时实时判断网络链接请求，如有更好的方法可以fork
extension UIDevice {
    static var isNetworkConnect: Bool {
        let network = NetworkReachabilityManager()
        return network?.isReachable ?? true // 无返回就默认网络已连接
    }
}

/// 错误处理
/// - Parameters:
///   - code: code码
///   - message: 错误消息
///   - needShowFailAlert: 是否显示网络请求失败的弹框
///   - failure: 网络请求失败的回调
private func errorHandler(code: Int,isErrNetwork: Bool, message: String, failure: RequestResultClosure?) {
    print("发生错误：\(code)--\(message)")
    let model = ResponseModel()
    model.code = code
    model.message = message
    model.isErrNetwork = isErrNetwork
    failure?(model)
}



/// 预判断后台返回的数据有效性 如通过Code码来确定数据完整性等  根据具体的业务情况来判断  有需要自己可以打开注释
/// - Parameters:
///   - response: 后台返回的数据
///   - showFailAlet: 是否显示失败的弹框
///   - failure: 失败的回调
/// - Returns: 数据是否有效
private func validateRepsonse(response: [String: JSON]?, failure: RequestResultClosure?) -> Bool {
    /**
     var errorMessage: String = ""
     if response != nil {
     if !response!.keys.contains(codeKey) {
     errorMessage = "返回值不匹配：缺少状态码"
     } else if response![codeKey]!.int == 500 {
     errorMessage = "服务器开小差了"
     }
     } else {
     errorMessage = "服务器数据开小差了"
     }
     
     if errorMessage.count > 0 {
     var code: Int = 999
     if let codeNum = response?[codeKey]?.int {
     code = codeNum
     }
     if let msg = response?[messageKey]?.stringValue {
     errorMessage = msg
     }
     errorHandler(code: code, message: errorMessage, showFailAlet: showFailAlet, failure: failure)
     return false
     }
     */
    
    return true
}

